Содержание
Предподготовка
Набор данных для анализа
Описание датасета
Формальное описание заданий
Предобработка
Визуальный анализ
Для визуализаций используется библиотека plotly.express
Раскомментируйте команду в следующей ячейке для ее установки при необходимости
#!pip install "plotly==5.8.2"
import numpy as np
import pandas as pd
import plotly.express as px
#import plotly.graph_objects as go
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# %config IPCompleter.greedy=True
# %config InlineBackend.figure_format = 'retina'
# псевдо-объект для промежуточных, масок и прочего
class _WorkData: pass
churn = _WorkData()
# Исходный набор данных
churn.data = pd.read_csv('data/churn.zip')
churn.data.head()
| RowNumber | CustomerId | Surname | CreditScore | Geography | Gender | Age | Tenure | Balance | NumOfProducts | HasCrCard | IsActiveMember | EstimatedSalary | Exited | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 15634602 | Hargrave | 619 | France | Female | 42 | 2 | 0.00 | 1 | 1 | 1 | 101348.88 | 1 |
| 1 | 2 | 15647311 | Hill | 608 | Spain | Female | 41 | 1 | 83807.86 | 1 | 0 | 1 | 112542.58 | 0 |
| 2 | 3 | 15619304 | Onio | 502 | France | Female | 42 | 8 | 159660.80 | 3 | 1 | 0 | 113931.57 | 1 |
| 3 | 4 | 15701354 | Boni | 699 | France | Female | 39 | 1 | 0.00 | 2 | 0 | 0 | 93826.63 | 0 |
| 4 | 5 | 15737888 | Mitchell | 850 | Spain | Female | 43 | 2 | 125510.82 | 1 | 1 | 1 | 79084.10 | 0 |
churn.data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10000 entries, 0 to 9999 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 RowNumber 10000 non-null int64 1 CustomerId 10000 non-null int64 2 Surname 10000 non-null object 3 CreditScore 10000 non-null int64 4 Geography 10000 non-null object 5 Gender 10000 non-null object 6 Age 10000 non-null int64 7 Tenure 10000 non-null int64 8 Balance 10000 non-null float64 9 NumOfProducts 10000 non-null int64 10 HasCrCard 10000 non-null int64 11 IsActiveMember 10000 non-null int64 12 EstimatedSalary 10000 non-null float64 13 Exited 10000 non-null int64 dtypes: float64(2), int64(9), object(3) memory usage: 1.1+ MB
| Наименование столбца | Описание |
|---|---|
| RowNumber | Номер строки таблицы (это лишняя информация, поэтому можете сразу от неё избавиться) |
| CustomerId | Идентификатор клиента |
| Surname | Фамилия клиента |
| CreditScore | Кредитный рейтинг клиента (чем он выше, тем больше клиент брал кредитов и возвращал их) |
| Geography | Страна клиента (банк международный) |
| Gender | Пол клиента |
| Age | Возраст клиента |
| Tenure | Сколько лет клиент пользуется услугами банка |
| Balance | Баланс на счетах клиента в банке |
| NumOfProducts | Количество услуг банка, которые приобрёл клиент |
| HasCrCard | Есть ли у клиента кредитная карта (1 — да, 0 — нет) |
| IsActiveMember | Есть ли у клиента статус активного клиента банка (1 — да, 0 — нет) |
| EstimatedSalary | Предполагаемая заработная плата клиента |
| Exited | Статус лояльности (1 — ушедший клиент, 0 — лояльный клиент) |
Итак, банк обращается к вам за помощью: он хочет разработать кампанию лояльности по удержанию клиентов, но для этого ему необходимо, чтобы вы выяснили основные причины оттока клиентов. Иными словами, нужно установить, чем ушедшие клиенты отличаются от лояльных и как между собой связаны различные признаки, определяющие клиентов.
Каково соотношение ушедших и лояльных клиентов? Покажите это на графике и дайте комментарий по соотношению.
Постройте график, показывающий распределение баланса пользователей, у которых на счету больше 2 500 долларов. Опишите распределение и сделайте выводы.
Посмотрите на распределение баланса клиента в разрезе признака оттока. Как различаются суммы на накопительном счёте ушедших и лояльных клиентов? Подумайте и напишите, с чем это может быть связано, что может не устраивать ушедших клиентов в банке.
Посмотрите на распределение возраста в разрезе признака оттока. В какой группе больше потенциальных выбросов? На какую возрастную категорию клиентов стоит обратить внимание банку?
Постройте график, который показывает взаимосвязь кредитного рейтинга клиента и его предполагаемой зарплаты. Добавьте расцветку по признаку оттока клиентов. Какова взаимосвязь между признаками? Если не видите явной взаимосвязи, укажите это.
Кто чаще уходит, мужчины или женщины? Постройте график, который иллюстрирует это.
Процент ушедших клиентов в каждой группе можно рассчитать как среднее по столбцу Exited (так как 1 — это ушедшие клиенты, а 0 — лояльные, среднее арифметическое по столбцу обозначает долю ушедших клиентов).
Как отток клиентов зависит от числа приобретённых у банка услуг? Для ответа на этот вопрос постройте многоуровневую столбчатую диаграмму.
Как влияет наличие статуса активного клиента на отток клиентов? Постройте диаграмму, иллюстрирующую это. Что бы вы предложили банку, чтобы уменьшить отток клиентов среди неактивных?
В какой стране доля ушедших клиентов больше? Постройте тепловую картограмму, которая покажет это соотношение на карте мира. Предположите, с чем это может быть связано.
Переведите числовой признак CreditScore в категориальный. Для этого воспользуйтесь функцией get_credit_score_cat(), которая приведена ниже. Примените её к столбцу CreditScore и создайте новый признак CreditScoreCat — категории кредитного рейтинга.
def get_credit_score_cat(credit_score):
if credit_score >= 300 and credit_score < 500:
return "Very_Poor"
elif credit_score >= 500 and credit_score < 601:
return "Poor"
elif credit_score >= 601 and credit_score < 661:
return "Fair"
elif credit_score >= 661 and credit_score < 781:
return "Good"
elif credit_score >= 781 and credit_score < 851:
return "Excellent"
elif credit_score >= 851:
return "Top"
elif credit_score < 300:
return "Deep"
Постройте сводную таблицу, строками которой являются категории кредитного рейтинга (CreditScoreCat), а столбцами — количество лет, в течение которых клиент пользуется услугами банка (Tenure). В ячейках сводной таблицы должно находиться среднее по признаку оттока (Exited) — доля ушедших пользователей.
На основе полученной сводной таблицы постройте тепловую карту с аннотацией. Найдите на тепловой карте категории клиентов, которые уходят чаще всего.
# убираем неиформативные признаки
churn_df = churn.data.drop(columns=['RowNumber', 'CustomerId', 'Surname'], axis=1)
# для единообразия в бинарных признаках
churn_df.rename(columns={'Exited':'isExited', 'HasCrCard':'isHasCrCard', 'IsActiveMember':'isActiveMember'}, inplace=True)
# бинарные признаки в ЧПИ
churn_df['Exited'] = churn_df['isExited'].replace({0:'No', 1:'Yes'})
churn_df['ActiveMember'] = churn_df['isActiveMember'].replace({0:'No', 1:'Yes'})
churn_df['HasCrCard'] = churn_df['isHasCrCard'].replace({0:'No', 1:'Yes'})
# категориальные
churn.category_columns =('Exited', 'HasCrCard', 'ActiveMember', 'Gender', 'Geography') #, 'Surname'
for column in churn.category_columns:
churn_df[column] = pd.Categorical(churn_df[column])
# оценим
churn_df[[*churn.category_columns]].describe()
| Exited | HasCrCard | ActiveMember | Gender | Geography | |
|---|---|---|---|---|---|
| count | 10000 | 10000 | 10000 | 10000 | 10000 |
| unique | 2 | 2 | 2 | 2 | 3 |
| top | No | Yes | Yes | Male | France |
| freq | 7963 | 7055 | 5151 | 5457 | 5014 |
# основные статистики по числовым признакам
churn_df[['Balance', 'CreditScore', 'EstimatedSalary', 'Age', 'Tenure', 'NumOfProducts']].describe().round(2)
# много 0 в Q25.
| Balance | CreditScore | EstimatedSalary | Age | Tenure | NumOfProducts | |
|---|---|---|---|---|---|---|
| count | 10000.00 | 10000.00 | 10000.00 | 10000.00 | 10000.00 | 10000.00 |
| mean | 76485.89 | 650.53 | 100090.24 | 38.92 | 5.01 | 1.53 |
| std | 62397.41 | 96.65 | 57510.49 | 10.49 | 2.89 | 0.58 |
| min | 0.00 | 350.00 | 11.58 | 18.00 | 0.00 | 1.00 |
| 25% | 0.00 | 584.00 | 51002.11 | 32.00 | 3.00 | 1.00 |
| 50% | 97198.54 | 652.00 | 100193.92 | 37.00 | 5.00 | 1.00 |
| 75% | 127644.24 | 718.00 | 149388.25 | 44.00 | 7.00 | 2.00 |
| max | 250898.09 | 850.00 | 199992.48 | 92.00 | 10.00 | 4.00 |
# и в целом
churn_df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10000 entries, 0 to 9999 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 CreditScore 10000 non-null int64 1 Geography 10000 non-null category 2 Gender 10000 non-null category 3 Age 10000 non-null int64 4 Tenure 10000 non-null int64 5 Balance 10000 non-null float64 6 NumOfProducts 10000 non-null int64 7 isHasCrCard 10000 non-null int64 8 isActiveMember 10000 non-null int64 9 EstimatedSalary 10000 non-null float64 10 isExited 10000 non-null int64 11 Exited 10000 non-null category 12 ActiveMember 10000 non-null category 13 HasCrCard 10000 non-null category dtypes: category(5), float64(2), int64(7) memory usage: 752.7 KB
Каково соотношение ушедших и лояльных клиентов? Покажите это на графике и дайте комментарий по соотношению.
# отток
churn.exited = churn_df.groupby('Exited')['Exited'].count().to_frame(name='Count')
churn.exited['Ratio (%)'] = round(churn.exited['Count'] / churn_df.shape[0] * 100, 2)
#print(churn.exited)
# в процентах
#fig=\
px.bar( data_frame=churn.exited, height=420, width=440,
title="Соотношение ушедших и лояльных клиентов",
x=churn.exited.index,
y='Ratio (%)',
color=churn.exited.index,
text = 'Ratio (%)',
)
#fig.show()
# # на круговой пожалуй не буду, ругають
# px.pie( data_frame=churn.exited.reset_index(), height=420, width=440,
# title="Соотношение ушедших и лояльных клиентов",
# names=('Лояльные','Ушедшие'), #'Exited'
# values='Ratio (%)',
# color='Exited',
# ).show()
Вывод по общему оттоку
Постройте график, показывающий распределение баланса пользователей, у которых на счету больше 2 500 долларов. Опишите распределение и сделайте выводы
churn.balance_gt_2k5 = churn_df['Balance'] > 2_500
# ??? Уточнить это на весь анализ или только про баланс
churn_df = churn_df[churn.balance_gt_2k5]
fig = px.histogram( data_frame=churn_df, width=700, height=440,
title='Распределение баланса пользователей(>2500$)',
x='Balance',
nbins=80,
marginal='box',
#histnorm='percent',
)
#fig.update_layout(bargap=0.05).show()
fig.update_layout(bargap=0.05)
fig.show()
Описание распределения
Посмотрите на распределение баланса клиента в разрезе признака оттока. Как различаются суммы на накопительном счёте ушедших и лояльных клиентов? Подумайте и напишите, с чем это может быть связано, что может не устраивать ушедших клиентов в банке.
fig = px.histogram( data_frame=churn_df , width=700, height=440,
title='Распределение клиетов по балансу в разрезе признака оттока',
x='Balance',
nbins=80,
color='Exited',
opacity=0.5,
marginal='box',
barmode='overlay',
)
fig.update_layout(bargap=0.05)
fig.show()
Описание
Посмотрите на распределение возраста в разрезе признака оттока. В какой группе больше потенциальных выбросов? На какую возрастную категорию клиентов стоит обратить внимание банку?
fig = px.histogram( data_frame=churn_df , width=800, height=500,
title='Распределение клиетов по возрасту в разрезе признака оттока',
x='Age',
color='Exited',
nbins=80,
marginal='box',
barmode='overlay',
opacity=0.5,
#histnorm='percent',
)
fig.update_layout(bargap=0.05)
fig.show()
Описание
Постройте график, который показывает взаимосвязь кредитного рейтинга клиента и его предполагаемой зарплаты. Добавьте расцветку по признаку оттока клиентов. Какова взаимосвязь между признаками? Если не видите явной взаимосвязи, укажите это.
# лайфхак - границы точек
# churn_df['_vsize'] = 1 # churn_df['isExited']+3
fig = px.scatter( data_frame=churn_df.assign(_vsize=1), # лайфхак - границы точек
width=740, height=620,
title='Взаимосвязь кредитного рейтинга клиента и его предполагаемой зарплаты',
x='CreditScore',
y='EstimatedSalary',
color='Exited',
opacity=0.5,
#
size='_vsize', # c окантовочкой глазу приятней
hover_data={'Exited':False, '_vsize':False}, # лишнее это
size_max=8,
#
range_x=[300, 900], # зафиксируем, а то скачет
)
fig.show()
Описание
Кто чаще уходит, мужчины или женщины? Постройте график, который иллюстрирует это.
Процент ушедших клиентов в каждой группе можно рассчитать как среднее по столбцу Exited (так как 1 — это ушедшие клиенты, а 0 — лояльные, среднее арифметическое по столбцу обозначает долю ушедших клиентов).
#
churn.gender_df = churn_df.groupby(['Gender', 'Exited'], as_index=False)['isExited'].count() # , as_index=False
churn.gender_df['isExited'] = round(churn.gender_df['isExited'] / churn_df.shape[0] * 100, 2)
#display(gender_df)
fig = px.bar( churn.gender_df, width=520, height=480,
title='Соотношение ушедших клиентов по признаку "Пол"',
x='Gender',
y='isExited',
color='Exited',
# barmode='stack',
barmode='group',
opacity=0.5,
labels={'isExited': 'Ratio (%)'},
)
#fig.update_xaxes(type='category', categoryorder='category ascending')
fig.show()
Как отток клиентов зависит от числа приобретённых у банка услуг? Для ответа на этот вопрос постройте многоуровневую столбчатую диаграмму.
churn.nprod_df = churn_df.groupby(['Exited','NumOfProducts'], as_index=False)['isExited'].count() # , as_index=False
churn.nprod_df['isExited'] = round(churn.nprod_df['isExited'] / churn_df.shape[0] * 100, 2)
#display(nprod_df)
fig = px.bar( churn.nprod_df, width=520, height=480,
title='Соотношение оттока от числа услуг',
x='NumOfProducts',
y='isExited',
color='Exited',
# barmode='stack',
barmode='group',
opacity=0.5,
labels={'isExited': 'Ratio (%)'},
)
#fig.update_xaxes(type='category', categoryorder='category ascending')
fig.show()
Описание
Как влияет наличие статуса активного клиента на отток клиентов? Постройте диаграмму, иллюстрирующую это. Что бы вы предложили банку, чтобы уменьшить отток клиентов среди неактивных?
churn.active_df = churn_df.groupby(['Exited','ActiveMember'], as_index=False)['isExited'].count() # , as_index=False
#display(active_df)
churn.active_df['isExited'] = round(churn.active_df['isExited'] / churn_df.shape[0] * 100, 2)
#display(active_df)
fig = px.bar( churn.active_df, width=520, height=480,
title='Соотношение оттока клиентов от активности',
x='ActiveMember',
y='isExited',
color='Exited',
# barmode='stack',
barmode='group',
opacity=0.5,
labels={'isExited': 'Ratio (%)'},
)
#fig.update_xaxes(type='category', categoryorder='category ascending')
fig.show()
Описание
В какой стране доля ушедших клиентов больше? Постройте тепловую картограмму, которая покажет это соотношение на карте мира. Предположите, с чем это может быть связано.
churn.country = churn_df.groupby('Geography')['isExited'].mean().mul(100).round(2)
churn.country_df = churn.country.to_frame(name='Count').reset_index()
#display(churn.country_df)
fig = px.choropleth( data_frame=churn.country_df,
width=600, height=480,
title='Тепловая карта оттока клиентов по странам в %',
locations="Geography",
locationmode = "country names",
color="Count",
color_continuous_scale='OrRd', # 'Reds'
range_color=[0, churn.country_df['Count'].max()],
scope="europe",
labels={'Count':'Ratio (%)'},
)
fig.update_geos(fitbounds="locations", visible = False, showcountries=True, )
#fig.update_layout(margin={"r":0,"t":80,"l":0,"b":0})
#fig.update_layout(showlegend=True,)
fig.show()
Описание
fig = px.histogram(
data_frame=churn_df[churn_df['Geography']=='Germany'] ,
width=800, height=520,
title='Распределение клиетов в Германии по возрасту в разрезе признака оттока',
x='Age',
color='Exited',
nbins=60,
marginal='box',
barmode='overlay',
opacity=0.5,
#histnorm='percent',
category_orders={'Exited':['No','Yes']}
)
fig.update_layout(bargap=0.05)
fig.show()
fig = px.histogram(
data_frame=churn_df[churn_df['Geography'].isin(['France','Spain'])] ,
width=800, height=520,
title='Распределение во Франции и Испании по возрасту в разрезе признака оттока',
x='Age',
color='Exited',
nbins=60,
marginal='box',
barmode='overlay',
opacity=0.5,
category_orders={'Exited':['No','Yes']}
)
fig.update_layout(bargap=0.05)
fig.show()
Примечание
Переведите числовой признак CreditScore в категориальный. Для этого воспользуйтесь функцией get_credit_score_cat(). Примените её к столбцу CreditScore и создайте новый признак CreditScoreCat — категории кредитного рейтинга.
Постройте сводную таблицу, строками которой являются категории кредитного рейтинга (CreditScoreCat), а столбцами — количество лет, в течение которых клиент пользуется услугами банка (Tenure). В ячейках сводной таблицы должно находиться среднее по признаку оттока (Exited) — доля ушедших пользователей.
На основе полученной сводной таблицы постройте тепловую карту с аннотацией. Найдите на тепловой карте категории клиентов, которые уходят чаще всего.
def get_credit_score_cat(credit_score):
if credit_score >= 300 and credit_score < 500:
return "Very_Poor"
elif credit_score >= 500 and credit_score < 601:
return "Poor"
elif credit_score >= 601 and credit_score < 661:
return "Fair"
elif credit_score >= 661 and credit_score < 781:
return "Good"
elif credit_score >= 781 and credit_score < 851:
return "Excellent"
elif credit_score >= 851:
return "Top"
elif credit_score < 300:
return "Deep"
#CreditScoreCat
churn_df['CreditScoreCat'] = churn_df['CreditScore'].apply(get_credit_score_cat)
churn_df['CreditScoreCat'] = pd.Categorical(churn_df['CreditScoreCat'], ordered=True,
categories=['Deep', 'Very_Poor', 'Poor', 'Fair', 'Good', 'Excellent', 'Top']
)
#churn_df['CreditScoreCat']
churn_df['Tenure Years'] = churn_df['Tenure'] + 1
churn.age_pivot = churn_df.pivot_table(
columns='Tenure Years',
index='CreditScoreCat',
values='isExited',
aggfunc=np.mean
).mul(100).round(2)
#churn.age_pivot
fig = px.imshow(churn.age_pivot,
width=860, height=480,
title="Тепловая карта оттока клиентов по категории кредитного\n рейтинга и годам обслуживания (%)",
text_auto='.0f',
color_continuous_scale='Blues', # "Viridis"
)
fig.show()
Описание
# версионинг. сверим часы ;)
import sys
import platform
import plotly
def version_control(you_only=False):
module_name = ['OS', 'Python', 'Numpy', 'Pandas', 'Plotly']
module_main = ['Windows-7 SP1', '3.7.9', '1.21.6', '1.3.5', '5.8.2']
module_version = [
platform.platform(),
sys.version.split()[0] , # +' x'+sys.version.split()[8]
np.__version__,
pd.__version__,
plotly.__version__,
]
if you_only:
return pd.Series(module_version, module_name).to_frame(name='Version')
else:
return pd.DataFrame({'You Version':module_version, 'My Version': module_main, }, index=module_name)
print('Module version control\n',version_control())
Module version control
You Version My Version
OS Windows-7-6.1.7601-SP1 Windows-7 SP1
Python 3.7.9 3.7.9
Numpy 1.21.6 1.21.6
Pandas 1.3.5 1.3.5
Plotly 5.8.2 5.8.2
SF DST-148 Дмитрий Орлов Январь 2023